#include "LockFreeList.h"
#include "Runtime/Platform/PlatformAPI.h"
namespace Alice{
    #define PLATFORM_CACHE_LINE_SIZE 64
    void LockFreeTagCounterHasOverflowed()
    {
        Sleep(1);
    }
    void LockFreeLinksExhausted(uint32 TotalNum)
    {
    }
    static void ChangeMem(int64 Delta)
    {
    }
    void* LockFreeAllocLinks(SIZE_T AllocSize)
    {
        ChangeMem(AllocSize);
        return malloc(AllocSize);
    }
    void LockFreeFreeLinks(SIZE_T AllocSize, void* Ptr)
    {
        ChangeMem(-int32(AllocSize));
        free(Ptr);
    }
    class LockFreeLinkAllocator_TLSCache : public Noncopyable
    {
        enum
        {
            NUM_PER_BUNDLE = 64,
        };

        typedef FLockFreeLinkPolicy::TLink TLink;
        typedef FLockFreeLinkPolicy::TLinkPtr TLinkPtr;

    public:

        LockFreeLinkAllocator_TLSCache()
        {
            TlsSlot = PlatformTLS::AllocTlsSlot();
        }
        ~LockFreeLinkAllocator_TLSCache()
        {
            PlatformTLS::FreeTlsSlot(TlsSlot);
            TlsSlot = 0;
        }

        /**
        * Allocates a memory block of size SIZE.
        *
        * @return Pointer to the allocated memory.
        * @see Free
        */
        TLinkPtr Pop()
        {
            FThreadLocalCache& TLS = GetTLS();

            if (!TLS.PartialBundle)
            {
                if (TLS.FullBundle)
                {
                    TLS.PartialBundle = TLS.FullBundle;
                    TLS.FullBundle = 0;
                }
                else
                {
                    TLS.PartialBundle = GlobalFreeListBundles.Pop();
                    if (!TLS.PartialBundle)
                    {
                        int32 FirstIndex = FLockFreeLinkPolicy::LinkAllocator.Alloc(NUM_PER_BUNDLE);
                        for (int32 Index = 0; Index < NUM_PER_BUNDLE; Index++)
                        {
                            TLink* Event = FLockFreeLinkPolicy::IndexToLink(FirstIndex + Index);
                            Event->DoubleNext.Init();
                            Event->SingleNext = 0;
                            Event->Payload = (void*)UPTRINT(TLS.PartialBundle);
                            TLS.PartialBundle = FLockFreeLinkPolicy::IndexToPtr(FirstIndex + Index);
                        }
                    }
                }
                TLS.NumPartial = NUM_PER_BUNDLE;
            }
            TLinkPtr Result = TLS.PartialBundle;
            TLink* ResultP = FLockFreeLinkPolicy::DerefLink(TLS.PartialBundle);
            TLS.PartialBundle = TLinkPtr(UPTRINT(ResultP->Payload));
            TLS.NumPartial--;
            ResultP->Payload = nullptr;
            return Result;
        }

        /**
        * Puts a memory block previously obtained from Allocate() back on the free list for future use.
        *
        * @param Item The item to free.
        * @see Allocate
        */
        void Push(TLinkPtr Item)
        {
            FThreadLocalCache& TLS = GetTLS();
            if (TLS.NumPartial >= NUM_PER_BUNDLE)
            {
                if (TLS.FullBundle)
                {
                    GlobalFreeListBundles.Push(TLS.FullBundle);
                }
                TLS.FullBundle = TLS.PartialBundle;
                TLS.PartialBundle = 0;
                TLS.NumPartial = 0;
            }
            TLink* ItemP = FLockFreeLinkPolicy::DerefLink(Item);
            const uint64 LocalNext=ItemP->DoubleNext.mAtomicProxy.load();
            ItemP->DoubleNext.mAtomicProxy=LockFreeLinkNode::MakeNode(0,LockFreeLinkNode::GetCounterAndState(LocalNext));
            ItemP->SingleNext = 0;
            ItemP->Payload = (void*)UPTRINT(TLS.PartialBundle);
            TLS.PartialBundle = Item;
            TLS.NumPartial++;
        }

    private:

        /** struct for the TLS cache. */
        struct FThreadLocalCache
        {
            TLinkPtr FullBundle;
            TLinkPtr PartialBundle;
            int32 NumPartial;

            FThreadLocalCache()
                : FullBundle(0)
                , PartialBundle(0)
                , NumPartial(0)
            {
            }
        };

        FThreadLocalCache& GetTLS()
        {
            FThreadLocalCache* TLS = (FThreadLocalCache*)PlatformTLS::GetTlsValue(TlsSlot);
            if (!TLS)
            {
                TLS = new FThreadLocalCache();
                PlatformTLS::SetTlsValue(TlsSlot, TLS);
            }
            return *TLS;
        }

        /** Slot for TLS struct. */
        uint32 TlsSlot;

        /** Lock free list of free memory blocks, these are all linked into a bundle of NUM_PER_BUNDLE. */
        FLockFreePointerListLIFORoot<PLATFORM_CACHE_LINE_SIZE> GlobalFreeListBundles;
    };

    static LockFreeLinkAllocator_TLSCache GLockFreeLinkAllocator;

    void FLockFreeLinkPolicy::FreeLockFreeLink(FLockFreeLinkPolicy::TLinkPtr Item)
    {
        GLockFreeLinkAllocator.Push(Item);
    }

    FLockFreeLinkPolicy::TLinkPtr FLockFreeLinkPolicy::AllocLockFreeLink()
    {
        FLockFreeLinkPolicy::TLinkPtr Result = GLockFreeLinkAllocator.Pop();
        return Result;
    }
    FLockFreeLinkPolicy::TAllocator FLockFreeLinkPolicy::LinkAllocator;
}
